package com.googlecode.transmutant

import org.apache.log4j.*

class Stepper implements Steppable { 
	{
		BasicConfigurator.configure()
		LogManager.rootLogger.level = Level.INFO
	}
	
	def log = LogManager.getLogger(Stepper.class);
	
	def steps = []
	
	void run() {
		step(steps)
	}
	
	void step(def steps) {
		
		if(steps instanceof Collection) {
			Collections.sort(steps, new TypeOrderComparator())
		}
		
		Map<String, Object> outs = new HashMap<String, Object>()
		
		for (step in steps) {
			def fields = step.class.declaredFields
			fields.each {f->
				if (f.isAnnotationPresent(In.class)){
					def a = f.getAnnotation(In.class)
					step.putAt f.name, outs.get(a.name())
					if (outs.get(a.name()) == null) {
						log.info("@In '${f.name}' of '${step.class}' is null")
					}
				}
			}
			
			def methodList = []
			
			step.class.methods.each {m->
				if (m.isAnnotationPresent(Step.class)){
					def a = m.getAnnotation(Step.class)
					def methodPosition = a.order()
					if (methodPosition > methodList.size()){
						methodPosition = methodList.size()
					}
					methodList.add(methodPosition, m)
				}
			}
			
			methodList.each{m->
				def parameterAnnotationsArray = m.parameterAnnotations
				def parameters = []
				parameterAnnotationsArray.each {parameterAnnotationArray->
					
					def parameterAnnotation = parameterAnnotationArray.find { it instanceof In  }
					
					if (parameterAnnotation == null){
						parameters.add null
						log.info("Found non @In parameters on method '${m.name}' of '${step.class}'. These will be set to null")
					}
					else {
						def value = outs.get(parameterAnnotation.name())
						parameters.add(value)
						if (value == null){
							log.info("@In '${parameterAnnotation.name()}' on method '${m.name}' of '${step.class}' is null")
						}
					}
				}
				
				
				def returnValue = step.invokeMethod(m.name, parameters.toArray())
				if (m.isAnnotationPresent(Out.class)){
					def annotationOut = m.getAnnotation(Out.class)
					outs.put annotationOut.name(), returnValue
					if (returnValue == null) {
						log.info("@Out '${annotationOut.name()}' on method '${m.name}' of '${step.class}' is null")
					}
				}
			}
			
			fields.each {f->
				if (f.isAnnotationPresent(Out.class)){
					def a = f.getAnnotation(Out.class)
					def value = step.getAt(f.name)
					outs.put a.name(), value
					if (value == null) {
						log.info("@Out '${a.name()}' of '${step.class}' is null")
					}
				}
			}
		}
	}
}
